#include <Ape/Ext/SharedLibrary.h>

#include <Ape/String.h>
#include <Ape/IO/FS/Path.h>
#include <Ape/IO/FS/EFileNotFound.h>
#include <Ape/IO/Console.h>
#include <Ape/EIndexOutOfRange.h>

#include <dlfcn.h>

namespace Ape {
	namespace Ext {
		using Ape::IO::FS::Path;
		using Ape::IO::FS::FSObject;
		
		SharedLibrary::SharedLibrary (const Path& path)
				: FSObject (path)
				, m_Handle (NULL)
		{
		}
		SharedLibrary::~SharedLibrary() {
			Close();
		}
		void SharedLibrary::Close() {
			if (m_Handle) {
				dlclose (m_Handle);
				m_Handle = NULL;
			}
		}
		void SharedLibrary::Open() {
			Close();
			get_Stat();
			
			char* csFName = NULL;
			m_Path.ToString().ToLatin1 (&csFName);
			m_Handle = dlopen (csFName, RTLD_LAZY);
			del_a (csFName);
			
			if (!m_Handle) {
				const char* dlErr = dlerror();
				String sErrDesc = L"";
				if (dlErr) {
					sErrDesc = dlErr;
				}
				throw Ape::IO::FS::EFileNotFound (L"Could not open shared library '" + m_Path.ToString() + L"':" + sErrDesc);
			}
		}
		
		void* SharedLibrary::Resolve (const String& symbol) {
			Open();
			char* csSymbol = NULL;
			symbol.ToString().ToLatin1 (&csSymbol);
			void* pEntity = dlsym (m_Handle, csSymbol);
			del_a (csSymbol);
			
			if (!pEntity)
				throw Ape::EIndexOutOfRange (L"Specified symbol could not be resolved: " + symbol);
				
			return pEntity;
		}
		
		Object* SharedLibrary::Activate (const String& className) {
			String symbol = GetActivatorNameForClass(className);
			
			//Ape::IO::StdOut << symbol << Ape::IO::NewLine;
			
			typedef Object* (*activator_t)();
			activator_t activator = ResolveTyped<activator_t>(symbol);
			return activator();
		}
		
		String SharedLibrary::GetActivatorNameForClass (const String& className) {
			return L"APE_new__" + className.Replace("::", "__");
		}
		
		void SharedLibrary::DisposeObject(Ape::Object* obj) {
			typedef void (*disposer_t)(Ape::Object* obj);
			disposer_t disposer = ResolveTyped<disposer_t>(L"APE_dispose_Object");
			disposer(obj);
		}
	}
}
